package com.keqiang.utils

import android.graphics.Canvas
import android.graphics.Paint
import android.text.Layout
import android.text.StaticLayout
import android.text.TextPaint

/**
 * 绘制相关工具
 *
 * @author Created by wanggaowan on 2022/7/20 16:03
 */
object PaintUtils {

    /**
     * 绘制文本，主要是处理绘制多行文本且超出行数显示省略符号
     * @param canvas 画布，从坐标0，0位置开始绘制，如果需要在特定起始坐标开始绘制，则调用此方法之前需要调用[Canvas.translate(dx,dy)]
     * @param paint 画笔，设置文本的颜色，大小等数据，建议不要通过[paint]设置对齐方式，paint始终设置为[Paint.Align.LEFT]。
     * 对齐方式使用[align]进行设定，如果paint设置了对齐方式，则paint和[align]将相互作用，可能出现非预期效果
     * @param align 文本绘制对齐方式
     * @param ellipsize 超出最大行数是否显示省略号，`false`则直接截断超出的部分
     * @param width 文本可显示的最大宽度，<= 0则不进行绘制
     */
    @JvmStatic
    @JvmOverloads
    fun drawText(canvas: Canvas, paint: TextPaint, text: CharSequence, width: Int, maxLines: Int = 1,
                 align: Layout.Alignment = Layout.Alignment.ALIGN_NORMAL, ellipsize: Boolean = false) {
        if (width <= 0) {
            return
        }

        var splitText =
            CharSequenceUtils.autoSplitText(text, paint, width, maxLines + 1)
        val split = splitText.split("\n")
        val layoutCount = split.size
        val layout = if (layoutCount > maxLines) {
            val builder = StringBuilder()
            for (i in 0 until maxLines) {
                if (i > 0) {
                    builder.append("\n")
                }
                builder.append(split[i])
            }

            splitText = builder.toString()
            val splitTextSize = splitText.replace("\n", "").length
            val newText = if (ellipsize && splitTextSize > 0) {
                val dotWidth = paint.measureText("...")
                var str = splitText.substring(splitText.length - 1, splitText.length)
                if (paint.measureText(str) >= dotWidth) {
                    splitText.substring(0, splitText.length - 1) + "..."
                } else if (splitTextSize > 1) {
                    str = splitText.substring(splitText.length - 2, splitText.length)
                    if (paint.measureText(str) >= dotWidth) {
                        splitText.substring(0, splitText.length - 2) + "..."
                    } else if (splitTextSize > 2) {
                        splitText.substring(0, splitText.length - 3) + "..."
                    } else {
                        "..."
                    }
                } else {
                    "..."
                }
            } else {
                splitText
            }
            StaticLayout(newText, paint, width, align, 1f, 0f, false)
        } else {
            StaticLayout(splitText, paint, width, align, 1f, 0f, false)
        }
        layout.draw(canvas)
    }

    /**
     * 获取指定文本大小的画笔[paint]、指定绘制宽度[width]及指定最大行数[maxLines]后文本最大绘制高度。
     * 如果[width] <= 0,则高度无法测量，此时返回0
     */
    fun getTextHeight(paint: TextPaint, text: CharSequence, width: Int, maxLines: Int): Int {
        if (width <= 0) {
            return 0
        }

        var layout =
            StaticLayout(text, paint, width, Layout.Alignment.ALIGN_NORMAL, 1f, 0f, false)
        val layoutCount = layout.lineCount
        if (layoutCount > maxLines) {
            val lineEnd = layout.getLineEnd(maxLines - 1)
            if (lineEnd != -1) {
                val newText = text.substring(0, lineEnd)
                layout =
                    StaticLayout(newText, paint, width, Layout.Alignment.ALIGN_NORMAL, 1f, 0f, false)
            }
        }
        return layout.height
    }

    /**
     * 获取指定文本大小的画笔[paint]、指定绘制宽度[width]后文本需要绘制的行数。
     * 如果[width] <= 0,则行数无法测量，此时返回1
     */
    @JvmStatic
    fun getTextLine(text: CharSequence, paint: TextPaint, width: Int): Int {
        if (width <= 0) {
            return 1
        }

        val layout =
            StaticLayout(text, paint, width, Layout.Alignment.ALIGN_NORMAL, 1f, 0f, false)
        return layout.lineCount
    }

    /**
     * 获取单个字符的高度
     * @param includePadding `true`:取值[Paint.FontMetrics.descent] - [Paint.FontMetrics.ascent]
     *
     *`false`:取值[Paint.FontMetrics.bottom] - [Paint.FontMetrics.top]
     */
    @JvmStatic
    @JvmOverloads
    fun Paint.getChartHeight(textSize: Float? = null, includePadding: Boolean = true): Float {
        textSize?.let {
            this.textSize = it
        }
        val fm: Paint.FontMetrics = this.fontMetrics
        return if (includePadding) {
            fm.bottom - fm.top
        } else {
            fm.descent - fm.ascent
        }
    }

    /**
     * 获取居中baseLine，如果需要将文本与y轴某一点（a）垂直居中对齐，则只需将需要 a + 此值 = 绘制文本的y轴坐标
     */
    @JvmStatic
    @JvmOverloads
    fun Paint.getCenterBaseline(textSize: Float? = null, includePadding: Boolean = true): Float {
        textSize?.let {
            this.textSize = it
        }

        val fm = this.fontMetrics
        return if (includePadding) {
            (fm.bottom - fm.top) / 2 - fm.bottom
        } else {
            (fm.descent - fm.ascent) / 2 - fm.descent
        }
    }
}